/*******************************************************************************
 * Copyright (c) 2012 Philip Douglas.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 ******************************************************************************/
package net.pdouglas.revisiondroid;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.AlertDialog;

import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;

import android.preference.PreferenceManager;
import android.util.Log;

import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;

import net.pdouglas.revision.Timer;

public class RevisionDroid extends SherlockActivity {
    //Constants
    private static final String TAG = TimerService.class.getSimpleName();

    private Handler mHandler;
    private WakeLock mWakeLock;
    private boolean mPowerConnected = false;
    private PowerStateReceiver mPSR;
    
    // Views
    private RelativeLayout mRevisionColumn;
    private RelativeLayout mRelaxColumn;
    private RelativeLayout mPauseColumn;

    private Counter  mRevisionCounter;
    private Counter  mRelaxCounter;

    private Button   mRevisionButton;
    private Button   mRelaxButton;
    private Button   mPauseButton;
    
    private ProgressBar mReviseBar;
    private ProgressBar mBreakBar;

	private boolean mBeingListenedTo = false;
	private boolean mConnectedToService = false;
	
	private int currentMode = Timer.MODE_OFF;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mHandler = new Handler();
        sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPrefs.registerOnSharedPreferenceChangeListener(spChanged);
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, 
            "ScreenOn");
        
        mPSR = new PowerStateReceiver(this);

        // Get the Views that will be interacted with
        mRevisionColumn  = (RelativeLayout) findViewById(R.id.revise_column);
        mRelaxColumn     = (RelativeLayout) findViewById(R.id.break_column);
        mPauseColumn     = (RelativeLayout) findViewById(R.id.pause_layout);

        mRevisionCounter = (Counter) findViewById(R.id.revise_counter);
        mRelaxCounter    = (Counter) findViewById(R.id.break_counter);

        // Get the buttons that need listeners and give them one
        mRevisionButton = (Button) findViewById(R.id.revise_button);
        mRelaxButton    = (Button) findViewById(R.id.break_button);
        mPauseButton    = (Button) findViewById(R.id.pause_button);
        
        mReviseBar		= (ProgressBar) findViewById(R.id.revise_bar);
        mBreakBar		= (ProgressBar) findViewById(R.id.break_bar);

        OnClickListener listener = new OnClickListener(this);
        mRevisionButton.setOnClickListener(listener);
        mRelaxButton.setOnClickListener(listener);
        mPauseButton.setOnClickListener(listener);
        
        mRevisionCounter.setOnClickListener(listener);
        mRelaxCounter.setOnClickListener(listener);
        
        mReviseBar.setOnClickListener(listener);
        mBreakBar.setOnClickListener(listener);

        Log.i(TAG, "Activity created");
    }
    
    void startMyService() {
    	boolean running = isMyServiceRunning();
    	if (!running || !mConnectedToService) {
	        Intent basicIntent = new Intent(TimerService.class.getName());
	        
	        if (!running) {
	        	startService(basicIntent);
	        }
	
	        bindService(basicIntent, serviceConnection, 0);
    	}
    	if (!mBeingListenedTo) {
    		addListener();
    	}
    }
    
    // Courtesy of http://stackoverflow.com/questions/600207/android-check-if-a-service-is-running
    private boolean isMyServiceRunning() {
        ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (TimerService.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }


    private void stopListening() {
        try {
            mApi.removeListener(mCollectorListener);
            mBeingListenedTo = false;
            unbindService(serviceConnection);
        }
        catch (Throwable t) {
            Log.w(TAG, "Failed to unbind from the service", t);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        stopListening();

        Log.i(TAG, "Activity destroyed");
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override    
    protected void onStart() {
    	super.onStart();
    }

    @Override    
    protected void onResume() {
    	super.onResume();
    	updateWakeLock();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_POWER_CONNECTED);
        filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
        registerReceiver(mPSR, filter);
        startMyService();
    }

    @Override    
    protected void onPause() {
    	super.onPause();
        wakeUnlock();
        
        unregisterReceiver(mPSR);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus && mApi != null) {
            update();
        }
    }

    protected void update() {
        try {
            final TimerState state = mApi.getTimerState();
            mHandler.post(new Runnable() {
                public void run() {
                    mRevisionCounter.setTime(state.reviseTime);
                    mRelaxCounter.setTime(state.relaxTime);
                    mReviseBar.setProgress(progressSeconds(state.reviseTime%60));
                    mBreakBar.setProgress(progressSeconds(state.relaxTime%60));
                    updateHilights(state.mode);
                }
            });
        }
        catch (RemoteException r) {
            Log.w(TAG, "Activity: Failed to get Timer State on tick", r);
        }
    }
    
    private int progressSeconds(int time) {
    	if (time >= 0) {
    		return time % 60;
    	} 
    	else {
    		return 60 - (-time % 60);
    	}
    }

    private void setMode(int mode) {
        try {
            mApi.setMode(mode);
            currentMode = mode;
        }
        catch (RemoteException r) {
            Log.w(TAG, "Activity: Failed to set mode.", r);
        }
    }
    
    private void updateHilights(int mode) {
    	if (currentMode == mode) {
    		return;
    	}
    	switch(mode) {
    	case Timer.MODE_OFF:
    		break;
    	case Timer.MODE_PAUSED:
    		showPaused();
    		break;
    	case Timer.MODE_RELAX:
    		showRelaxation();
    		break;
    	case Timer.MODE_REVISE:
    		showRevision();
    		break;
    	}
    	currentMode = mode;
    }

    private void hilight(RelativeLayout rl) {
        rl.setBackgroundResource(R.color.selected);
    }

    private void lowlight(RelativeLayout rl) {
        rl.setBackgroundResource(0);
    }

    private void showRevision() {
        hilight(mRevisionColumn);
        lowlight(mRelaxColumn);
        mPauseColumn.setBackgroundResource(0);
    }

    protected void reviseButton() {
        showRevision();
        setMode(Timer.MODE_REVISE);
    }

    private void showRelaxation() {
        lowlight(mRevisionColumn);
        hilight(mRelaxColumn);
        mPauseColumn.setBackgroundResource(0);
    }

    protected void relaxButton() {
        showRelaxation();
        setMode(Timer.MODE_RELAX);
    }

    private void showPaused() {
        lowlight(mRevisionColumn);
        lowlight(mRelaxColumn);
        mPauseColumn.setBackgroundResource(R.color.selected);
    }

    protected void pauseButton() {
        showPaused();
        setMode(Timer.MODE_PAUSED);
    }

    public void showMode() {
        try {
            int mode = mApi.getMode();
            switch (mode) {
                case Timer.MODE_REVISE:
                    showRevision();
                    break;
                case Timer.MODE_RELAX:
                    showRelaxation();
                    break;
                case Timer.MODE_PAUSED:
                    showPaused();
                    break;
            }
        }
        catch (RemoteException r) {
            Log.e(TAG, "Failed to get mode", r);
        }
        catch (NullPointerException n) {
            return;
        }
    }

    // Service connection stuff
    private TimerApi mApi;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder service) {
        	mConnectedToService = true;
            mApi = TimerApi.Stub.asInterface(service);
            addListener();
            try {
                // Push config
            	// TODO: Merge this with ~375
            	float currentRatio = mApi.getRatio();
            	float ratio = Float.valueOf(sharedPrefs.getString("prefRatio", "2"));
            	if (currentRatio != ratio) {
            		mApi.setRatio(1/ratio);
            	}
            }
            catch (RemoteException r) {
                Log.w(TAG, "Failed to get config from service", r);
            }
            update();
            showMode();
            pushZeroRelaxAlarmPref();
        }

		public void onServiceDisconnected(ComponentName arg0) {
			mConnectedToService = false;
		}
    };
    
    private void addListener() {
        try{
            mApi.addListener(mCollectorListener);
            mBeingListenedTo = true;
        }
        catch (RemoteException e) {
            Log.e(TAG, "Failed to add listener", e);
            mBeingListenedTo = false;
        }
        catch (NullPointerException e) {
        	Log.w(TAG, "Adding listener failed");
        	mBeingListenedTo = false;
        }
    }

    private TimerListener.Stub mCollectorListener = new TimerListener.Stub() {
        public void handleTick() throws RemoteException {
            update();
        }
    };

    /* Menu Code */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    protected void pushZeroRelaxAlarmPref() {
    	try {
    		mApi.setZeroRelaxAlarmPref(sharedPrefs.getBoolean("prefZeroRelaxAlarm", false));
    	}
    	catch (RemoteException r) {
    		Log.w(TAG, "Unable to set zero relax alarm preference", r);
    	}
	}

	@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.menu_reset:
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage(getString(R.string.dialog_reset)).setPositiveButton("Yes", resetDialogClickListener)
                .setNegativeButton("No", resetDialogClickListener).show();
            break;
        case R.id.menu_settings:
        	if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        		startActivity(new Intent(getBaseContext(), SettingsMenuGB.class));
        	}
        	else {
        		startActivity(new Intent(getBaseContext(), SettingsMenu.class));
        	}
            break;
        default:
            return super.onOptionsItemSelected(item);
        }
        return true;
    }

    /* Preferences code */
    private SharedPreferences.OnSharedPreferenceChangeListener spChanged 
            = new SharedPreferences.OnSharedPreferenceChangeListener() {
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            try {
                if (key.equals("prefRatio")) {
                    float ratio = Float.valueOf(prefs.getString(key, "0"));
                    ratio = 1/ratio;
                    mApi.setRatio(ratio);
                }
                else if (key.equals("prefScreenOn") || key.equals("prefPowerOnly")) {
                	updateWakeLock();
                }
                else if (key.equals("prefZeroRelaxAlarm")) {
                	pushZeroRelaxAlarmPref();
                }
            }
            catch (RemoteException r) {
                Log.e(TAG, "Sending preference to service failed.", r);
            }
        }
    };
    private SharedPreferences sharedPrefs;
    
    private void updateWakeLock() {
		boolean screenOn = sharedPrefs.getBoolean("prefScreenOn", false);
		boolean powerOnly = sharedPrefs.getBoolean("prefPowerOnly", true);
		if (screenOn && ((powerOnly && mPowerConnected) || !powerOnly)) {
			wakeLock();
		} 
		else {
			wakeUnlock();
		}
    }
    
    private void wakeLock() {
		if (!mWakeLock.isHeld()) mWakeLock.acquire();
	}
    
    private void wakeUnlock() {
		if (mWakeLock.isHeld()) mWakeLock.release();
    }
    
    protected void powerConnect() {
		mPowerConnected = true;
		updateWakeLock();
	}
    
    protected void powerDisconnect() {
		mPowerConnected = false;
		updateWakeLock();
	}

    // Dialogs
    // From http://stackoverflow.com/questions/2478517/how-to-display-a-yes-no-dialog-box-in-android
    DialogInterface.OnClickListener resetDialogClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            switch (which){
            case DialogInterface.BUTTON_POSITIVE:
            	try {
	                mApi.reset();
	            }
	            catch (RemoteException r) {
	                Log.e(TAG, "Unable to reset Timer Service.", r);
	            }
	            pauseButton();
                break;
            case DialogInterface.BUTTON_NEGATIVE:
                break;
            }
        }
    };
}
